Videos:
https://open.fing.edu.uy/courses/fsi/21/
https://open.fing.edu.uy/courses/fsi/22/
https://open.fing.edu.uy/courses/fsi/23/
https://open.fing.edu.uy/courses/fsi/24/
https://open.fing.edu.uy/courses/fsi/25/
https://open.fing.edu.uy/courses/fsi/26/
https://open.fing.edu.uy/courses/fsi/27/
Diapositivas:
[[aplicaciones_introduccion.pdf]]
[[aplicaciones_diseno_seguro.pdf]]
[[aplicaciones_riesgos.pdf]]
[[aplicaciones_modelado_amenzas.pdf]]
[[aplicaciones_autenticacion.pdf]]
[[aplicaciones_sesiones_web.pdf]]
[[aplicaciones_owasp_top_ten.pdf]]
En una máquina stand-alone, estamos en control de los componentes de software que hacen entrada al sistema. El software es seguro si puede manejar entradas intencionalmente malformadas. Analizaremos las causas generales de las vulnerabilidades en el software y propondremos pautas para mitigar los problemas.
Presentar las causas básicas que llevan a fallas de seguridad del software:
Malware: Malicious software.
-> Un bug no es malware si no hay mala intención.
Ejemplo de problema: Una aplicación debería dar a los usuarios acceso al subdirectorio A/B/C. Los usuarios ingresan el archivo como entrada. El path del archivo se construye como A/B/C/entrada. Podrían escalar en el árbol de directorios usando ../
.
../../../../etc/passwd
.../
.La codificación UTF-8 se define para usar Unicode en sistemas que fueron diseñados para ASCII (RFC 2259). Los chars ASCII son representados por los bytes ASCII (0x00 - 0x7F).
Ejemplo: el signo © es U00A9, en UTF-8 se escribe como 0xC2 0xA9. Por ese motivo, un único carácter puede tener distinta representación: en el caso de '/': 1 byte – 2F, 2 byte – C0 AF, 3 byte – E0 80 AF.
Pueden dividirse en 2 categorías dependiendo de quién causa la interferencia.
En general, debe verificarse el código por cualquier par de operaciones que puedan fallar si se ejecuta código arbitrario entre ellas. Notar que cargar, modificar y salvar una variable compartida no es un proceso atómico. Problemas característicos: archivos temporales.
Hay situaciones en las cuales un programa debe asegurar que tiene derechos exclusivos sobre algún recurso. Cualquier sistema que haga locking debe lidiar con los problemas estándar de los locks (deadlocks, livelocks, etc.). Típicamente en Unix se utiliza la existencia de un archivo como lock, por ser portable.
Una transacción atómica es una abstracción de una operación que debería ejecutarse como una unidad.
Debemos tratar de identificar patrones generales. En el caso de software inseguro, el patrón que se repite es el de abstracciones de programación como variable, array, integer. Los problemas de la seguridad en el software pueden ser direccionados en la arquitectura del procesador, lenguajes de programación, etc.
Muchos ataques de buffer overflow sobre-escriben información de control. Estos ataques pueden ser prevenidos mediante el uso de características del hardware para proteger la información de control. El procesador de Intel Itanium tiene un registro separado para la dirección de retorno.
Podemos prevenir bugs en el software mediante el uso de un lenguaje de programación que nos prevenga de hacer errores. La seguridad en los tipos garantiza la ausencia de errores no atrapados. Ejemplos son Java y C#. Se puede garantizar mediante el chequeo estático y en tiempo de ejecución.
strcpy
, sprintf
, gets
.char *strcpy (char *strDest, const char *strOrig)
.strncpy
, snprintf
, fgets
.La inspección de código manual es tediosa y tendiente a los errores por lo tanto es deseable alguna forma de automatización. Herramientas de este tipo exploran el código en búsqueda de errores potenciales. Es bueno para detectar tipos de problemas conocidos pero no garantiza que no hay vulnerabilidades.
El testeo es parte integral del desarrollo de software, normalmente será usada para demostrar la correctitud de la funcionalidad. En el testing de seguridad, la situación difiere algo dado que tenemos que mostrar la correctitud de la funcionalidad de seguridad. Esto implica tener alguna idea de las amenazas potenciales.
Una técnica usada es la de Data Mutation. Esto envía entradas mal formadas a las interfaces de los programas. Se testea cómo se manejan las entradas inesperadas. El software puede caerse si se intenta crear un recurso que ya existe. Para los datos, los casos de test deben incluir los bordes, los tipos distintos, tamaños, etc.
El principio de mínimo privilegio aplica dos veces. Al escribir código, ahorrar el requerir privilegios para ejecutar el código. Si este código se ve comprometido, el daño es limitado. También debe usarse nuevamente al instalar nuevos sistemas. No dar a los usuarios más derechos de acceso de los necesarios y no activar las opciones que no se necesitan.
Cuando se descubre una vulnerabilidad, el código afectado debe arreglarse. La nueva versión debe ser testeada, se debe hacer el patch correspondiente y, por último, los usuarios deben instalarlo. Por ello, no son sólo los vendedores de software los que deben actuar, también lo debe hacer la comunidad de usuarios.
Cada funcionalidad agrega una cierta cantidad de riesgo a la aplicación. Reducir el riesgo general mediante la reducción del área de ataque.
Por defecto, la experiencia de uso de una aplicación debería ser segura. Debería ser el usuario quien pudiera habilitar funcionalidades adicionales (si tiene permiso).
Las cuentas deben tener el menor privilegio requerido para realizar los procesos de negocio.
Donde un control es razonable, más controles que piensan en los riesgos de diferentes formas son mejores. Los controles, cuando se usan en profundidad, pueden hacer que las vulnerabilidades graves sean extraordinariamente difíciles de explotar.
Las aplicaciones fallan regularmente en procesar transacciones por diversas razones. Fallar no debe dar al usuario privilegios adicionales, y no debería devolver información sensible no necesaria (ej., logs, DB queries).
Muchas aplicaciones usan servicios de terceros para obtener datos adicionales. El principio dice que no se debe confiar en estos servicios desde el punto de vista de la seguridad. Quiere decir que debe verificarse la validez de los datos enviados por el tercero y no asignarle altos niveles de privilegio a dichos servicios dentro de la aplicación.
Un control clave para evitar fraudes es separar las responsabilidades. Por ejemplo, alguien que solicita una nueva computadora no puede además firmar para habilitarlo, y no debería recibirla en forma personal. Los administradores no deberían ser usuarios de la aplicación. Deberían poder cambiar políticas de contraseñas, pero no comprar bienes como si fueran otros usuarios, por ejemplo.
Es un control débil y casi siempre falla cuando es el único. La seguridad de la aplicación no debe depender de mantener un secreto como una URL oculta. No confundir con tener una clave, que es un secreto y debe mantenerse en secreto.
La superficie de ataque y la simplicidad van de la mano.
Luego de identificado un problema es importante crear un test para encontrar la causa. Si se han usado patrones de diseño, ese problema puede estar esparcido a lo largo del código. Escribir un test garantiza que no haya regresiones.
Si les pregunto: ¿cómo defiendo mi aplicación?.... ¿de qué?
Las técnicas de TM usan los cuatro pasos siguientes:
Las razones en contra del uso de estos tipos de autenticación son:
<form … AUTOCOMPLETE="off">
para los campos del form.“Completely automated public Turing test to tell computers and humans apart”.
Las aplicaciones “finas” de clientes guardan de forma innata datos locales. Por diseño, el protocolo HTTP no tiene estado, y no mantiene nativamente una conexión entre el navegador cliente y el software del servidor. Las aplicaciones web no triviales pueden requerir mantener un estado a lo largo de una sesión.
Esta es la principal desventaja de HTTP para la implementación de aplicaciones complejas. #parcial
¿Cómo se implementan? Algo compartido entre el servidor Web y el cliente:
Algunos frameworks usan áreas compartidas en el disco del servidor web para guardar datos de sesión. Por ejemplo, PHP usa el /tmp en Unix y C:/windows/temp en Windows por defecto. Pueden comprometer a la aplicación si el servidor web se comparte o es comprometido.
Muchas aplicaciones web toman medidas en contra de intentos de adivinar claves. Es menos común para las aplicaciones web detectar muchos intentos de continuar sesiones basadas en adivinar identificadores de sesión. Los servidores de aplicación rara vez hacen logs o auditan dichos intentos.
Si el identificador se transmite via un parámetro en la URL en vez de una cookie, los GETs pueden guardarse en la historia, el cache, etc. Usar pedidos mediante POST puede ayudar a aliviar este problema. Hay propuestas de usar la IP dentro de la asociación a las sesiones - ¿Qué pasa con NAT/Proxies?
Para reducir el riesgo de secuestro de sesiones y ataques por fuerza bruta, el servidor HTTP puede sin esfuerzo expirar y regenerar los tokens. Esto decrece la ventana de oportunidad para un ataque por replay o fuerza bruta.
Algunos tokens (nonces) pueden usarse en conjunción con tokens específicos de sesión para proveer medidas de autenticidad en pedidos de clientes. El cliente del otro lado de la sesión es el mismo que pidió la página en la misma sesión. Pueden guardarse en cookies o en las propias query strings, y deben ser completamente al azar.
Crear un mapeo entre el cliente y el token en el lado del servidor. Esta técnica debería incrementar la dificultad de usar fuerza bruta con los tokens de autenticación de la sesión.
Si se generan tokens predecibles, un atacante no necesita capturar las variables de sesión de los usuarios remotos. Puede simplemente adivinar algunos identificadores de sesión. Los tokens de sesión deben ser únicos, no predecibles, y resistentes a ingeniería reversa.
Si puede capturarse un token en tránsito, es posible hacer secuestro de dicha sesión. Es más probable que suceda si sólo usamos HTTP. Nunca debe transmitirse en texto claro. Usar otro token para áreas que requieran mayor nivel de seguridad.
Se debe invalidar el token y borrar el identificador de sesión al salir el usuario. En general, las cookies se asocian a la vida de la ventana del browser. En una máquina compartida, el identificador de sesión puede quedar en el navegador y ser utilizado por el próximo usuario.
Como cualquier dato, la variable de sesión debe ser validada para asegurar que está de la forma correcta. Que no contiene caracteres no esperados, y es válida en la tabla de sesiones. Por ejemplo, usar bytes nulos para truncar objetos de sesión y debido a errores de codificación se comparaba el largo del string más corto.
Usar un manejador de sesión robusto y bien conocido dentro de algún framework de aplicación. Siempre usar las versiones más actualizadas porque pueden contener bugs. Si se está en duda, no deberían tomarse riesgos y guardar la información sensible del estado en sesiones en el servidor.
Siempre guardar datos del lado del servidor. Los datos para tomar decisiones no críticas, como por ejemplo el lenguaje o el tema, pueden guardarse en datos en el cliente. Los campos ocultos nunca deberían usarse para guardar información de estado sensible.
Los atacantes a menudo intentarán explotar defectos sin parchear o acceder a cuentas predeterminadas, páginas no utilizadas, archivos y directorios desprotegidos, etc. para obtener acceso o conocimiento no autorizado del sistema.
La explotación de la deserialización es algo difícil, ya que los exploits como son distribuidos raramente funcionan sin cambios o ajustes en el código de exploit subyacente.
Se afecta mediante:
Ocurre cuando un desarrollador expone una referencia a un objeto de implementación interno
Permite:
Recordar que del lado del servidor siempre deberían haber validaciones, aunque haya del lado del cliente.
Caso:
SELECT * FROM 'tabla'
WHERE 'user' = '$_POST[usuario]' && 'pass' = '$_POST[pass]'
user: pepe' or '1'='1
pass: pepe' or '1'='1
o
user: pepe' or '1'='1';--
pass: no importa pq queda comentada esta parte
Una web es vulnerable si la cookie de sesión no tiene la bandera HTTP Only.
Cross-Site Request Forgery (CSRF) is an attack that forces authenticated users to submit a request to a Web application against which they are currently authenticated. CSRF attacks exploit the trust a Web application has in an authenticated user.
Unlike cross-site scripting (XSS), which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has in a user’s browser.
This is the most simple CSRF attack to perform. For example you receive an e-mail with the following content:
<a href="http://bank.com/transfer?account_number_from=123456789&account_number_to=987654321&amount=100000">View my Pictures!</a>
If the user is still logged in to the website of bank.com this simple GET request will transfer money from one account to another. Of course in most cases the website might have multiple controls to approve the request.
Vulnerabilidades:
En el lab se hizo Bypass authentication a partir de un Buffer Overflow.